package com.hxh.generater.springBoot.simple.gen.crud;

import cn.hutool.core.util.StrUtil;
import com.hxh.config.Constants;
import com.hxh.entity.Project;
import com.hxh.entity.ProjectDetail;
import com.hxh.entity.ProjectTableFiledConfig;
import com.hxh.entity.po.ClassInfo;
import com.hxh.entity.po.FieldName;
import com.hxh.generater.springBoot.util.ProcessWriteLine;
import com.hxh.generater.springBoot.util.methodParse.ParseJavaMethod;
import com.hxh.generater.springBoot.util.methodParse.entity.JavaClassMethod;
import com.hxh.util.GenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.hxh.config.Constants.ProjectDirSuffix;

/**
 * @ProjectName: AICode
 * @Package: com.hxh.generater
 * @Author: HXH
 * @Description:
 * @Date: 2023/11/28 15:08
 */
@Slf4j
public class GenService {

    public static void addHistoryAnnotation(Project project, String serviceName, ClassInfo
            classInfo) throws IOException {
        ProjectDetail backendProject = project.getBackendProject();
        String pathPre = backendProject.getProjectDir() + backendProject.getProjectName() + "\\" + ProjectDirSuffix + "\\"
                + backendProject.getProjectGroup().replaceAll("\\.", "\\\\\\\\");
        String filePath = pathPre + "\\service\\" + serviceName + ".java";
        File oldFile = new File(filePath);
        if (!oldFile.exists()) {
            log.error("添加历史记录注解失败，类不存在！" + filePath);
            return;
        }
        List<JavaClassMethod> javaClassMethodList = ParseJavaMethod.parser(oldFile);
        List<Integer> updateMethodStartLine = new ArrayList<>();
        for (JavaClassMethod javaClassMethod : javaClassMethodList) {
            if ("update".equals(javaClassMethod.getMethodName())) {
                updateMethodStartLine.add(javaClassMethod.getStartLine());
            }
        }
        if (CollectionUtils.isEmpty(updateMethodStartLine)) {
            log.warn("添加历史记录注解失败，不存在update方法！");
            return;
        }
        File newFile = GenUtil.newFile(project, "service", serviceName);
        oldFile = new File(pathPre + "\\service\\" + serviceName + ".java.bak");


        StringBuilder ids = new StringBuilder("{");

        for (FieldName fieldName : classInfo.getIdNameList()) {
            ids.append("\"").append(fieldName.getJavaName()).append("\",");
        }
        if (ids.length() > 1) {
            ids.deleteCharAt(ids.length() - 1);
        }
        ids.append("}");

        try (FileWriter fileWriter = new FileWriter(newFile);
             BufferedWriter writer = new BufferedWriter(fileWriter);
             BufferedReader br = new BufferedReader(new FileReader(oldFile))) {
            String line;

            int currentLine = 0;
            int updateMethodStartLineIndex = 0;
            int updateMethodStartLineMaxLen = updateMethodStartLine.size();
            boolean hasAnnotation = false;
            boolean hasImportHistoryAnnotation = false;
            while ((line = br.readLine()) != null) {
                currentLine++;

                if (currentLine == 3 && !hasImportHistoryAnnotation && !line.contains("HistoryAnnotation")) {
                    line = "import " + backendProject.getProjectGroup() + ".annotation.HistoryAnnotation;\n" + line;
                }
                if (StringUtils.isNotBlank(line)) {
                    if (line.contains("HistoryAnnotation")) {
                        if (hasImportHistoryAnnotation) {
                            line = "";
                        }
                        hasImportHistoryAnnotation = true;
                    }
                    if (updateMethodStartLineIndex < updateMethodStartLineMaxLen) {
                        int listStartLine = updateMethodStartLine.get(updateMethodStartLineIndex);
                        if (currentLine == listStartLine && !hasAnnotation) {
                            line = "    @HistoryAnnotation(ids = " + ids + ")\n" + line;
                            updateMethodStartLineIndex++;
                        } else if (currentLine + 3 >= listStartLine) {
                            if (line.contains("@HistoryAnnotation")) {
                                hasAnnotation = true;
                            }
                        } else {
                            hasAnnotation = false;
                        }
                    }
                    writer.write(line);
                }
                writer.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String generationServiceSelectById(ClassInfo classInfo) {
        String className = classInfo.getClassName();
        String daoName = className + "Dao";
        String daoNameEntity = StringUtils.uncapitalize(daoName);

        StringBuilder res = new StringBuilder("    public " + className + " selectById(");
        StringBuilder params = new StringBuilder();
        StringBuilder params3 = new StringBuilder();
        for (FieldName fieldName : classInfo.getIdNameList()) {
            params.append(fieldName.getJavaType()).append(" ").append(fieldName.getJavaName()).append(",");
            params3.append(fieldName.getJavaName()).append(",");
        }
        if (params.length() > 0) {
            params.deleteCharAt(params.length() - 1);
        }
        if (params3.length() > 0) {
            params3.deleteCharAt(params3.length() - 1);
        }
        res.append(params).append("){\n");

        res.append("        if(");
        StringBuilder params2 = new StringBuilder();
        for (FieldName fieldName : classInfo.getIdNameList()) {
            params2.append(fieldName.getJavaName()).append(" == null ||");
        }
        if (params2.length() > 0) {
            params2.deleteCharAt(params2.length() - 1);
            params2.deleteCharAt(params2.length() - 1);
        }
        params2.append(") {\n");
        params2.append("          return null;\n");
        params2.append("        }\n");
        res.append(params2);
        res.append("        return ").append(daoNameEntity).append(".selectById(").append(params3).append(");\n");
        res.append("    }");
        return res.toString();
    }

    private static String generationServiceDeleteById(ClassInfo classInfo) {
        String className = classInfo.getClassName();
        String daoName = className + "Dao";
        String daoNameEntity = StringUtils.uncapitalize(daoName);

        StringBuilder res = new StringBuilder("    public boolean deleteById(");
        StringBuilder params = new StringBuilder();
        StringBuilder params3 = new StringBuilder();
        for (FieldName fieldName : classInfo.getIdNameList()) {
            params.append(fieldName.getJavaType()).append(" ").append(fieldName.getJavaName()).append(",");
            params3.append(fieldName.getJavaName()).append(",");
        }
        if (params.length() > 0) {
            params.deleteCharAt(params.length() - 1);
        }
        if (params3.length() > 0) {
            params3.deleteCharAt(params3.length() - 1);
        }
        res.append(params).append("){\n");
        res.append("        return ").append(daoNameEntity).append(".deleteById(").append(params3).append(") > 0;\n");
        res.append("    }");
        return res.toString();
    }

    private static Map<String, String> generationAttachFile(ClassInfo classInfo) {
        List<FieldName> fieldNameList = classInfo.getFieldNameList();
        StringBuilder addRes = new StringBuilder();
        StringBuilder updateRes = new StringBuilder();
        Map<String, String> resMap = null;
        for (FieldName fieldName : fieldNameList) {
            ProjectTableFiledConfig projectTableFiledConfig = fieldName.getProjectTableFiledConfig();
            if (projectTableFiledConfig != null && "img".equals(projectTableFiledConfig.getShowType())) {
                String getFieldNameStr = "get" + StrUtil.upperFirst(fieldName.getJavaName()) + "()";
                addRes.append("            if (pojo." + getFieldNameStr + " != null) {\n");
                addRes.append("                sysFileService.attachFile(pojo." + getFieldNameStr + ");\n");
                addRes.append("            }\n");

                updateRes.append("            if (!ObjectUtil.equal(old." + getFieldNameStr + ", pojo." + getFieldNameStr + ")) {\n");
                updateRes.append("                if (old." + getFieldNameStr + " != null) {\n");
                updateRes.append("                    sysFileService.deleteById(old." + getFieldNameStr + ");\n");
                updateRes.append("                }\n");
                updateRes.append("                if (pojo." + getFieldNameStr + " != null) {\n");
                updateRes.append("                    sysFileService.attachFile(pojo." + getFieldNameStr + ");\n");
                updateRes.append("                }\n");
                updateRes.append("            }\n");
            }
        }
        if (addRes.length() > 0) {
            addRes.insert(0, "        if (res) {\n");
            addRes.append("        }\n");

            String className = classInfo.getClassName();
            String daoNameEntity = StrUtil.lowerFirst(className) + "Dao";

            updateRes.insert(0, "        if (res) {\n");
            updateRes.insert(0, "        " + className + " old = " + daoNameEntity + ".selectById(pojo.getId());\n");
            updateRes.append("        }\n");

            resMap = new HashMap<>(2);
            resMap.put("insert", addRes.toString());
            resMap.put("update", updateRes.toString());
        }

        return resMap;
    }

    public static void generationServiceClass(Project project, ClassInfo classInfo) throws IOException {
        generationServiceClass(project, classInfo, null);
    }

    public static String generationServiceClass(String packageName, ClassInfo classInfo) throws IOException {
        return generationServiceClass(null, classInfo, packageName);
    }

    /**
     * @param project:   项目对象
     * @param classInfo: 类对象
     * @return void
     * @author HXH
     * @description: 根据项目和类信息生成server类
     * @date 2024/3/21
     **/
    public static String generationServiceClass(Project project, ClassInfo classInfo, String packageName) throws IOException {
        if (project == null && StringUtils.isBlank(packageName)) {
            log.error("generationServiceClass中，project参数和packageName参数不能同时为空!");
            throw new RuntimeException("generationServiceClass中，project参数和packageName参数不能同时为空!");
        }
        boolean returnModel = false;
        if (project == null) {
            returnModel = true;
        }

        String className = classInfo.getClassName();
        String entityName = StrUtil.lowerFirst(classInfo.getClassName());

        String serviceName = className + "Service";
        String daoName = className + "Dao";
        String daoNameEntity = StringUtils.uncapitalize(daoName);

        String serviceTempName = serviceName + ".Template";

        File tempClassFile;
        if (returnModel) {
            tempClassFile = File.createTempFile(daoName, ".Template");
        } else {
            tempClassFile = GenUtil.newFile(project, "service", serviceTempName);
        }

        StringBuilder importAddition = new StringBuilder();
        StringBuilder autowiredAddition = new StringBuilder();

        String selectById = generationServiceSelectById(classInfo);
        String deleteById = generationServiceDeleteById(classInfo);

        Map<String, String> attachFileMap = generationAttachFile(classInfo);
        String attachFileAfterInsert = "";
        String attachFileAfterUpdate = "";
        if (attachFileMap != null) {
            attachFileAfterInsert = attachFileMap.get("insert");
            attachFileAfterUpdate = attachFileMap.get("update");
        }

        StringBuilder addCreatorBeforeInsert = new StringBuilder();
        StringBuilder addCreatorBeforeInsertList = new StringBuilder();
        StringBuilder addUpdaterBeforeUpdate = new StringBuilder();
        List<FieldName> fieldNameList = classInfo.getFieldNameList();
        for (FieldName fieldName : fieldNameList) {
            if (GenUtil.AutoProcessCreatorFields.equals(fieldName.getName())) {
                importAddition.append("import #GroupId#.entity.User;\n");
                importAddition.append("import #GroupId#.config.GetCurrentUserUtil;\n");
                addCreatorBeforeInsert.append("        User user = GetCurrentUserUtil.getSubject();\n" +
                        "        pojo.setCreator(user.getUsername());\n");

                addCreatorBeforeInsertList.append(
                        "        User user = GetCurrentUserUtil.getSubject();\n" +
                                "        for (#className#Input #entityName#Input : pojo) {\n" +
                                "            #entityName#Input.setCreator(user.getUsername());\n" +
                                "        }\n");

            } else if (GenUtil.AutoProcessUpdaterFields.equals(fieldName.getName())) {
                importAddition.append("import #GroupId#.entity.User;\n");
                importAddition.append("import #GroupId#.config.GetCurrentUserUtil;\n");
                addUpdaterBeforeUpdate.append("        User user = GetCurrentUserUtil.getSubject();\n" +
                        "        pojo.setUpdater(user.getUsername());\n");
            }
        }

        //先生成模板
        try {
            FileWriter fileWriter = new FileWriter(tempClassFile);
            BufferedWriter writer = new BufferedWriter(fileWriter);

            File template = new File(Constants.templatePathPrefix + "simple\\template\\serviceTemplate");
            BufferedReader br = new BufferedReader(new FileReader(template));
            String line;

            Map<String, String> replaceWord = new HashMap<>();
            replaceWord.put("deleteById", deleteById);
            replaceWord.put("selectById", selectById);
            replaceWord.put("attachFileAfterInsert", attachFileAfterInsert);
            replaceWord.put("attachFileAfterUpdate", attachFileAfterUpdate);
            replaceWord.put("addCreatorBeforeInsert", addCreatorBeforeInsert.toString());
            replaceWord.put("addCreatorBeforeInsertList", addCreatorBeforeInsertList.toString());
            replaceWord.put("addUpdaterBeforeUpdate", addUpdaterBeforeUpdate.toString());
            ProcessWriteLine processWriteLine = new ProcessWriteLine(replaceWord);
            while ((line = br.readLine()) != null) {
                if (StringUtils.isNotBlank(line)) {
                    if ("#attachFileAfterInsert#".equals(line.trim())) {
                        if (StringUtils.isNotBlank(attachFileAfterInsert)) {
                            importAddition.append("import cn.hutool.core.util.ObjectUtil;").append("\n");
                            autowiredAddition.append("    @Resource SysFileService sysFileService;").append("\n");
                        }
                    }
                }
                processWriteLine.writeLine(line, writer);
            }
            br.close();


            //加上addition\\serviceTemplateAddition文件下，与类名相同的文件夹下所有的文件
            File additionFile = new File(Constants.templatePathPrefix + "simple\\template\\addition\\serviceTemplateAddition\\" + serviceName);
            if (additionFile.exists() && additionFile.isDirectory()) {
                File[] additionFiles = additionFile.listFiles();
                if (additionFiles != null && additionFiles.length > 0) {
                    for (File file : additionFiles) {
                        br = new BufferedReader(new FileReader(file));
                        while ((line = br.readLine()) != null) {
                            ProcessWriteLine.processPerLine(line, importAddition, autowiredAddition, writer);
                        }
                        br.close();
                    }
                }
            }

            writer.newLine();
            writer.write("}");

            writer.close();
            fileWriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Map<String, String> replaceWord = new HashMap<>();
        replaceWord.put("importAddition", importAddition.toString());
        replaceWord.put("autowiredAddition", autowiredAddition.toString());
        replaceWord.put("GroupId", returnModel ? packageName : project.getBackendProject().getProjectGroup());
        replaceWord.put("className", className);
        replaceWord.put("daoName", daoName);
        replaceWord.put("serviceName", serviceName);
        replaceWord.put("daoNameEntity", daoNameEntity);
        replaceWord.put("entityName", entityName);

        List<JavaClassMethod> methodList = ParseJavaMethod.parser(tempClassFile.getAbsolutePath());
        List<JavaClassMethod> methodRepeatList = ParseJavaMethod.tagRepeatMethod(methodList);
        ProcessWriteLine processWriteLine = new ProcessWriteLine(replaceWord, methodList, methodRepeatList);
        try {
            BufferedWriter writer;
            FileWriter fileWriter = null;
            StringWriter stringWriter = null;
            if (returnModel) {
                stringWriter = new StringWriter();
                writer = new BufferedWriter(stringWriter);
            } else {
                File classFile = GenUtil.newFile(project, "service", serviceName);
                fileWriter = new FileWriter(classFile);
                writer = new BufferedWriter(fileWriter);
            }

            File template = tempClassFile;
            BufferedReader br = new BufferedReader(new FileReader(template));
            String line;

            int currentLine = 0;
            while ((line = br.readLine()) != null) {
                currentLine++;
                processWriteLine.writeLineWithRepeatMethod(line, writer, currentLine);
            }
            br.close();

            writer.close();
            if (fileWriter != null) {
                fileWriter.close();
            }

            String bakPath = tempClassFile.getAbsolutePath() + ".bak";
            File bakFile = new File(bakPath);
            if (tempClassFile.exists()) {
                tempClassFile.delete();
            }
            if (bakFile.exists()) {
                bakFile.delete();
            }
            if (returnModel) {
                return stringWriter.toString();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}
